#include "ItemModelPropertyBrowserAdaptor.h"
#include "PropertyManager.h"

#include "ItemModels/ItemModel.h"
#include "ItemModels/SpecificationTreeModel.h"

#include "Document.h"

#include <qtpropertybrowser/qtpropertybrowser.h>
#include <qtpropertybrowser/qtpropertymanager.h>
#include <qtpropertybrowser/qtvariantproperty.h>

// FIXME: let the model returns the enum metadata
// or add a method
// ItemModelPropertyBrowserAdaptor::setEnumMapping(int metaTypeId,
//                                                 QMap<int, QString> &mapping);
#include "LeIpc2581/enumtranslator.h"

#include <QAbstractItemModel>
#include <QDebug>

ItemModelPropertyBrowserAdaptor::ItemModelPropertyBrowserAdaptor(QObject *parent):
    QObject(parent),
    m_model(nullptr),
    m_browser(nullptr),
    m_manager(new PropertyManager(this)),
    m_factory(new QtVariantEditorFactory(this)),
    m_readOnly(false)
{

}

bool ItemModelPropertyBrowserAdaptor::isReadOnly() const
{
    return m_readOnly;
}

void ItemModelPropertyBrowserAdaptor::setItemModel(QAbstractItemModel *model)
{
    if (m_model == model)
        return;

    clear();
    m_model = model;
    populate();
}

void ItemModelPropertyBrowserAdaptor::setModelIndex(const QModelIndex &index)
{
    if (m_index == index)
        return;

    clear();
    m_index = index;
    populate();
}

void ItemModelPropertyBrowserAdaptor::setPropertyBrowser(QtAbstractPropertyBrowser *browser)
{
    if (m_browser == browser)
        return;

    clear();
    m_browser = browser;
    populate();
}

void ItemModelPropertyBrowserAdaptor::setReadOnly(bool readOnly)
{
    if (m_readOnly == readOnly)
        return;

    if (m_browser != nullptr && !m_readOnly)
        m_browser->unsetFactoryForManager(m_manager);

    m_readOnly = readOnly;

    if (m_browser != nullptr && !m_readOnly)
        m_browser->setFactoryForManager(m_manager, m_factory);

    emit readOnlyChanged(readOnly);
}

void ItemModelPropertyBrowserAdaptor::clear()
{
    m_manager->clear();
    if (m_browser != nullptr && !m_readOnly)
        m_browser->unsetFactoryForManager(m_manager);
}

void ItemModelPropertyBrowserAdaptor::populate()
{
    if (m_model == nullptr)
        return;
    if (!m_index.isValid())
        return;
    if (m_browser == nullptr)
        return;

    if (!m_readOnly)
        m_browser->setFactoryForManager(m_manager, m_factory);

    // Standard attributes
    {
        auto groupProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), "Attributes");

        for (int column =0; column < m_model->columnCount(m_index.parent()); column++)
        {
            const QString name = m_model->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
            const QModelIndex index = m_model->index(m_index.row(), column, m_index.parent());
            const QVariant value = m_model->data(index, Qt::EditRole);
            QtVariantProperty *property = nullptr;
            if (QMetaType(value.userType()).flags().testFlag(QMetaType::IsEnumeration))
            {
                property = m_manager->addProperty(QtVariantPropertyManager::enumTypeId(), name);
                const QStringList names = Ipc2581b::EnumTranslator::enumNames(value.userType());
                m_manager->setAttribute(property, "enumNames",
                                        QVariant(names));
            }
            else
            {
                property = m_manager->addProperty(value.type(), name);
            }
            if (property != nullptr)
            {
                property->setValue(value);
                groupProperty->addSubProperty(property);
            }
        }
        m_browser->addProperty(groupProperty);
    }

    // Specifications
    QVariant value = m_model->data(m_index, ItemModel::SpecificationModelRole);
    if (value.isValid() && value.canConvert<QStringList>() && !value.toStringList().isEmpty())
    {
        const Document *document = m_model->data(m_index, ItemModel::DocumentRole).value<const Document*>();
        if (document == nullptr)
        {
            qWarning() << QString("Unable to retrieve document for specification #%1 of '%2'")
                          .arg(m_index.row()).arg(/*m_model->name*/"?");
            return;
        }

        const SpecificationTreeModel *model = document->specificationTreeModel();
        if (model == nullptr)
        {
            qWarning() << QString("Unable to retrieve model for specification #%1 of '%2'")
                          .arg(m_index.row()).arg(/*m_model->name*/"?");
            return;
        }

        QtVariantProperty *groupProperty = nullptr;
        for (const QString& ref: value.toStringList())
        {
            const QModelIndex specIndex = model->index(ref);
            if (!specIndex.isValid())
            {
                qWarning() << QString("Specification with ID='%1' not found.").arg(ref);
                continue;
            }

            if (groupProperty == nullptr)
                groupProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), "Specifications");

            auto specProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), model->data(specIndex).toString());
            groupProperty->addSubProperty(specProperty);

            // TODO: Drill-down
            for (int categoryRow = 0; categoryRow < model->rowCount(specIndex); categoryRow++)
            {
                const QModelIndex categoryIndex = model->index(categoryRow, 0, specIndex);
                auto categoryProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), model->data(categoryIndex).toString());
                specProperty->addSubProperty(categoryProperty);

                for (int row = 0; row < model->rowCount(categoryIndex); row++)
                {
                    const QModelIndex nameIndex = model->index(row, SpecificationTreeModel::NameColumn, categoryIndex);
                    const QString name = model->data(nameIndex, Qt::DisplayRole).toString();

                    const QModelIndex valueIndex = model->index(row, SpecificationTreeModel::ValueColumn, categoryIndex);
                    const QVariant value = model->data(valueIndex, Qt::EditRole);

                    QtVariantProperty *property = m_manager->addProperty(value.type(), name);
                    if (property != nullptr)
                    {
                        property->setValue(value);
                        categoryProperty->addSubProperty(property);
                    }
                }
            }
        }
        m_browser->addProperty(groupProperty);
    }
}
